package models

import (
	"encoding/json"
	"github.com/ahmetb/go-linq/v3"
	"github.com/beego/beego/v2/client/orm"
	"time"
)

type Menu struct {
	BModel

	Memo         string `orm:"column(memo)" json:"memo"`
	ParentID     uint64 `orm:"column(parent_id)" json:"parent_id"`
	Url          string `orm:"column(url)" json:"url"`
	Name         string `orm:"column(name)" json:"name"`
	Status       uint8  `orm:"column(status)" json:"status"` //0->删除1->正常
	Sequence     uint64 `orm:"column(sequence)" json:"sequence"`
	MenuType     uint8  `orm:"column(menu_type)" json:"menu_type"`
	Code         string `orm:"column(code)" json:"code"`
	Icon         string `orm:"column(icon)" json:"icon"`
	OperatorType string `orm:"column(operate_type)" json:"operate_type"`
	Level        uint8  `orm:"column(level)" json:"level"`
}

//MarshalJSON 格式化时间
func (tb Menu) MarshalJSON() ([]byte, error) {

	type Alias Menu

	return json.Marshal(struct {
		Alias
		CreatedAt string `json:"created_at"`
		UpdatedAt string `json:"updated_at"`
	}{
		Alias:     (Alias)(tb),
		CreatedAt: tb.CreatedAt.Format("2006-01-02 15:04:05"),
		UpdatedAt: tb.UpdatedAt.Format("2006-01-02 15:04:05"),
	})
}

type MenuTree struct {
	Name   string     `json:"name"`
	Icon   string     `json:"icon"`
	Code   string     `json:"code"`
	Childs []MenuTree `json:"childs"`
	Url    string     `json:"url"`
}

func NewMenu() *Menu {
	return &Menu{}
}

func (tb *Menu) TableName() string {
	return MenuTable
}

func (tb *Menu) GetAll() (lst []Menu, err error) {

	db := orm.NewOrm()
	q := db.QueryTable(tb).Filter("status__gt", 0).OrderBy("sequence")
	_, err = q.All(&lst)
	return

}

func (tb *Menu) GetMenuTree(roleIDs []uint64) (tree []MenuTree, err error) {

	if len(roleIDs) <= 0 {
		tree = make([]MenuTree, 0)
		return
	}
	var (
		lst         []Menu
		roleMenuLst []RoleMenu
	)
	db := orm.NewOrm()
	q := db.QueryTable(tb).Filter("status__gt", 0)
	_, err = q.All(&lst)
	if err != nil {
		return
	}

	q = db.QueryTable(NewRoleMenu()).Filter("role_id__in", roleIDs)
	_, err = q.All(&roleMenuLst)
	if err != nil {
		return
	}

	linq.From(lst).WhereT(func(w Menu) bool {
		return linq.From(roleMenuLst).AnyWithT(func(a RoleMenu) bool { return a.MenuID == w.ID })
	}).ToSlice(&lst)
	//一级+二级
	linq.From(lst).WhereT(func(w Menu) bool { return w.MenuType == 1 }).OrderByT(func(o Menu) uint64 { return o.Sequence }).ForEachT(func(p Menu) {

		var curMenuTree MenuTree
		curMenuTree.Icon = p.Icon
		curMenuTree.Childs = make([]MenuTree, 0)
		curMenuTree.Code = p.Code
		curMenuTree.Name = p.Name
		curMenuTree.Url = p.Url
		//二级
		linq.From(lst).WhereT(func(w Menu) bool { return w.ParentID == p.ID }).OrderByT(func(o Menu) uint64 { return o.Sequence }).ForEachT(func(c Menu) {

			curMenuTree.Childs = append(curMenuTree.Childs, MenuTree{
				Icon: c.Icon,
				Code: c.Code,
				Name: c.Name,
				Url:  c.Url,
			})
		})
		tree = append(tree, curMenuTree)

	})
	return

}

func (tb *Menu) GetTreeView(rids []uint64) (tree []TreeView, err error) {

	var (
		lst []Menu
	)
	db := orm.NewOrm()
	q := db.QueryTable(tb).Filter("status__gt", 0)
	_, err = q.All(&lst)
	if err != nil {
		return
	}
	//一级+二级
	linq.From(lst).WhereT(func(w Menu) bool { return w.MenuType == 1 }).OrderByT(func(o Menu) uint64 { return o.Sequence }).ForEachT(func(p Menu) {

		curMenuDirNode := TreeView{
			ID:   p.ID,
			Text: p.Name,
			Pid:  p.ParentID,
			State: TreeViewNodeState{
				Checked: linq.From(rids).AnyWithT(func(a uint64) bool { return a == p.ID }),
			},
		}

		curMenuDirNode.Nodes = make([]TreeView, 0)

		//二级
		linq.From(lst).WhereT(func(w Menu) bool { return w.ParentID == p.ID }).OrderByT(func(o Menu) uint64 { return o.Sequence }).ForEachT(func(c Menu) {

			curMenuNode := TreeView{
				ID:   c.ID,
				Pid:  c.ParentID,
				Text: c.Name,
				State: TreeViewNodeState{
					Checked: linq.From(rids).AnyWithT(func(a uint64) bool { return a == c.ID }),
				},
			}
			//三级

			curMenuNode.Nodes = make([]TreeView, 0)
			curMenuDirNode.Nodes = append(curMenuDirNode.Nodes, curMenuNode)
		})
		tree = append(tree, curMenuDirNode)

	})
	return

}

func (tb *Menu) HasChild(id uint64) (has bool, err error) {

	db := orm.NewOrm()
	c, _ := db.QueryTable(tb).Filter("parent_id", id).Filter("status_gt", 0).Count()
	has = c > 0
	return
}

type DeleteMenuReq struct {
	ID uint64 `json:"id"`
}

func (tb *Menu) Remove(id, uid uint64) (err error) {

	now := time.Now()
	db := orm.NewOrm()
	tb.Status = 0
	tb.UpdatedAt = now
	tb.UpdatedBy = uid
	tb.ID = id
	_, err = db.Update(tb, "status", "updated_at", "updated_by")
	return

}
func (tb *Menu) CountByCode(code string) (count uint64, err error) {

	db := orm.NewOrm()
	c, e := db.QueryTable(tb).Filter("code", code).Count()
	count = uint64(c)
	err = e
	return
}
func (tb *Menu) Find(id uint64) (m *Menu, err error) {

	m = &Menu{}
	db := orm.NewOrm()
	q := db.QueryTable(tb).Filter("id", id)
	err = q.One(m)
	if m.ID <= 0 {
		m = nil
	}
	return
}

type AddMenuReq struct {
	Pid      uint64 `json:"pid,omitempty"`
	Name     string `json:"name"`
	Url      string `json:"url"`
	Icon     string `json:"icon"`
	Code     string `json:"code"`
	Memo     string `json:"memo"`
	Sequence uint64 `json:"sequence"`
	MenuType uint8  `json:"menu_type"`
	Level    uint8  `json:"level"`
}

func (tb *Menu) Add(uid uint64, req AddMenuReq) (err error) {

	now := time.Now()
	db := orm.NewOrm()
	tb.Status = 1
	tb.Name = req.Name
	tb.Url = req.Url
	tb.Code = req.Code
	tb.UpdatedAt = now
	tb.CreatedAt = now
	tb.CreatedBy = uid
	tb.UpdatedBy = uid
	tb.Memo = req.Memo
	tb.MenuType = req.MenuType
	tb.Icon = req.Icon
	tb.Sequence = req.Sequence
	tb.ParentID = req.Pid
	tb.Level = req.Level
	_, err = db.Insert(tb)

	return
}

type UpdateMenuReq struct {
	ID       uint64 `json:"id,omitempty"`
	Name     string `json:"name"`
	Url      string `json:"url"`
	Icon     string `json:"icon"`
	Code     string `json:"code"`
	Memo     string `json:"memo"`
	Sequence uint64 `json:"sequence"`
}

func (tb *Menu) Update(uid uint64, req UpdateMenuReq) (err error) {

	now := time.Now()
	db := orm.NewOrm()
	tb.Name = req.Name
	tb.Url = req.Url
	tb.Code = req.Code
	tb.UpdatedAt = now
	tb.UpdatedBy = uid
	tb.Memo = req.Memo
	tb.Icon = req.Icon
	tb.Sequence = req.Sequence
	tb.ID = req.ID
	_, err = db.Update(tb, "url", "code", "updated_at", "updated_by", "memo", "icon", "sequence", "name")

	return
}
